package com.tools.web.interceptor.access;

import com.tools.common.exception.ParseRuntimeException;
import com.tools.common.object.Note;
import com.tools.common.object.string.StrKit;
import com.tools.common.object.string.VerifyKit;

import java.time.LocalDateTime;
import java.util.Objects;

/**
 * 自定义时间表达式的实体包装类
 * */
@Note("自定义时间表达式的实体包装类")
public class OwnCron {

    @Note("【0 或负数】- 表示每年。否则那就是指定的年份。")
    private int year;

    @Note("【0 或负数】- 表示每月。否则取值 1 ~ 12")
    private int month;

    @Note("【0 或负数】- 表示月份中的每天。否则取值 1 ~ 31")
    private int day;

    @Note("【0 或负数】- 表示一周中的每天。否则取值 1 ~ 7")
    private int dayWeek;

    @Note("【负数】- 表示每小时。否则取值 0 ~ 23")
    private int hour = -1;

    @Note("【负数】- 表示每分钟。否则取值 0 ~ 59")
    private int minute = -1;

    @Note("【负数】- 表示每秒。否则取值 0 ~ 59")
    private int second = -1;

    /* **************************************************************************************
     *
     *          构造器
     *
     * **************************************************************************************
     * */

    public OwnCron() {}

    public OwnCron(String text) {
        this.parse(text);
    }

    public static OwnCron of() {
        return new OwnCron();
    }

    public static OwnCron of(String text) {
        return new OwnCron(text);
    }

    /* **************************************************************************************
     *
     *          Getter / Setter
     *
     * **************************************************************************************
     * */


    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        if(month > 12) throw new IllegalArgumentException("月份不能大于 12");
        this.month = month;
    }

    public int getDay() {
        return day;
    }

    public void setDay(int day) {
        if(private_checkIsLegalDayOfMonth(day)) {
            this.day = day;
            return;
        }
        throw new IllegalArgumentException(month + " 月对应的天数 " + day + " 不合法");
    }

    public int getDayWeek() {
        return dayWeek;
    }

    public void setDayWeek(int dayWeek) {
        if(dayWeek > 7) throw new IllegalArgumentException("一周内的天数 " + dayWeek + " 不合法");
        this.dayWeek = dayWeek;
    }

    public int getHour() {
        return hour;
    }

    public void setHour(int hour) {
        if(hour > 23) throw new IllegalArgumentException("小时的取值不合法");
        this.hour = hour;
    }

    public int getMinute() {
        return minute;
    }

    public void setMinute(int minute) {
        if(minute > 59) throw new IllegalArgumentException("分钟的取值不合法");
        this.minute = minute;
    }

    public int getSecond() {
        return second;
    }

    public void setSecond(int second) {
        if(second > 59) throw new IllegalArgumentException("秒的取值不合法");
        this.second = second;
    }

    @Override
    public String toString() {
        return String.valueOf(year) + ' ' +
                month + ' ' +
                day + ' ' +
                dayWeek + ' ' +
                hour + ' ' +
                minute + ' ' +
                second;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        OwnCron ownCron = (OwnCron) o;
        return year == ownCron.year && month == ownCron.month && day == ownCron.day && dayWeek == ownCron.dayWeek && hour == ownCron.hour && minute == ownCron.minute && second == ownCron.second;
    }

    @Override
    public int hashCode() {
        return Objects.hash(year, month, day, dayWeek, hour, minute, second);
    }

    public String formatDefault() {
        return (hasYear() ? year + " 年" : "每年") + ' ' +
                (hasMonth() ? month + " 月" : "每月") + ' ' +
                (hasDay() ? month + " 天" : "每天") + ' ' +
                (hasDayWeek() ? "周 " + dayWeek : "每周 " + dayWeek) + ' ' +
                (hasHour() ? hour + ":" : "每小时") + ' ' +
                (hasMinute() ? minute + ":" : "每分") + ' ' +
                (hasSecond() ? second + ":" : "每秒");
    }

    /* **************************************************************************************
     *
     *          功能方法
     *
     * **************************************************************************************
     * */

    public boolean hasYear() {
        return year > 0;
    }

    public boolean hasMonth() {
        return month > 0;
    }

    public boolean hasDay() {
        return day > 0;
    }

    public boolean hasDayWeek() {
        return dayWeek > 0;
    }

    public boolean hasHour() {
        return hour >= 0;
    }

    public boolean hasMinute() {
        return minute >= 0;
    }

    public boolean hasSecond() {
        return second >= 0;
    }


    @Note("解析字符串文本为本对象")
    public void parse(String text) {
        if(text == null || text.isEmpty()) return;
        String[] a = StrKit.split(text, " ");
        int length = a.length;
        if(length < 7) throw new ParseRuntimeException('【' + text + "】 不符合规范");
        for (int i = 0; i < length; i++) {
            String s = a[i];
            if(!VerifyKit.isInteger(s)) throw new ParseRuntimeException("表达式的每一项必须是数字");
            switch (i) {
                case 0 : this.setYear(Integer.parseInt(s)); break;
                case 1 : this.setMonth(Integer.parseInt(s)); break;
                case 2 : this.setDay(Integer.parseInt(s)); break;
                case 3 : this.setDayWeek(Integer.parseInt(s)); break;
                case 4 : this.setHour(Integer.parseInt(s)); break;
                case 5 : this.setMinute(Integer.parseInt(s)); break;
                case 6 : this.setSecond(Integer.parseInt(s)); break;
            }
        }
    }


    public boolean gteNow() {
        LocalDateTime now = LocalDateTime.now();
        if(this.hasYear() && now.getYear() >= this.getYear()) {
            return false;
        }
        if(this.hasMonth() && now.getMonthValue() >= this.getMonth()) {
            return false;
        }
        if(this.hasDay() && now.getDayOfMonth() >= this.getDay()) {
            return false;
        }
        if(this.hasDayWeek() && now.getDayOfWeek().getValue() >= this.getDayWeek()) {
            return false;
        }
        if(this.hasHour() && now.getHour() >= this.getHour()) {
            return false;
        }
        if(this.hasMinute() && now.getMinute() >= this.getMinute()) {
            return false;
        }
        return this.hasSecond() && now.getSecond() >= this.getSecond();
    }


    public boolean ltNow() {
        LocalDateTime now = LocalDateTime.now();
        if(this.hasYear() && now.getYear() < this.getYear()) {
            return true;
        }
        if(this.hasMonth() && now.getMonthValue() < this.getMonth()) {
            return true;
        }
        if(this.hasDay() && now.getDayOfMonth() < this.getDay()) {
            return true;
        }
        if(this.hasDayWeek() && now.getDayOfWeek().getValue() < this.getDayWeek()) {
            return true;
        }
        if(this.hasHour() && now.getHour() < this.getHour()) {
            return true;
        }
        if(this.hasMinute() && now.getMinute() < this.getMinute()) {
            return true;
        }
        return this.hasSecond() && now.getSecond() < this.getSecond();
    }
    
    
    /* **************************************************************************************
     *
     *          链式方法
     *
     * **************************************************************************************
     * */

    public OwnCron year(int year) {
        this.setYear(year);
        return this;
    }

    public OwnCron month(int month) {
        this.setMonth(month);
        return this;
    }

    public OwnCron day(int day) {
        this.setDay(day);
        return this;
    }

    public OwnCron dayWeek(int dayWeek) {
        this.setDayWeek(dayWeek);
        return this;
    }

    public OwnCron hour(int hour) {
        this.setHour(hour);
        return this;
    }

    public OwnCron minute(int minute) {
        this.setMinute(minute);
        return this;
    }

    public OwnCron second(int second) {
        this.setSecond(second);
        return this;
    }

    /* **************************************************************************************
     *
     *          私有逻辑
     *
     * **************************************************************************************
     * */

    @Note("校验天是否正确")
    private boolean private_checkIsLegalDayOfMonth(int day) {
        if(month < 1) {
            return day <= 31;
        }
        if(month == 2) {
            return day == 28 || day == 29;
        }
        if(month == 4 || month == 6 || month == 9 || month == 11) {
            return day == 30;
        }
        return day == 31;
    }
}
